<template>
  <Form
    ref="form"
    v-bind="curFormProps"
    :model="curFormData"
    @validate="onValidate"
  >
    <row :gutter="24">
      <template v-for="item in formRules" :key="item.field.toString()">
        <Col
          v-bind="Object.assign({}, col || {}, item.col)"
          v-show="item?.hide ? false : true"
        >
          <form-item v-bind="getItemProps(item)">
            <component
              :is="item.type"
              v-bind="getComProps(item.medelField, item.field, item.props)"
              v-on="getComEvents(item.medelField, item.field, item.on)"
            />
          </form-item>
        </Col>
      </template>
    </row>
  </Form>
</template>

<script lang="ts" setup>
import { cloneDeep } from 'lodash-es'
import { ref, unref, computed, watch } from 'vue'

import { Form, Row, Col, FormItem } from 'ant-design-vue'

import type { ColProps } from 'ant-design-vue'
import type { BwFormRuleModel, BwFormGridModel } from './model'

export interface BwModalFormProps {
  hideRequiredMark?: boolean

  col?: ColProps

  wrap?: BwFormGridModel

  formData?: Record<string, any>

  formRules?: Array<BwFormRuleModel>
}

const emit = defineEmits(['update:visible', 'validate', 'ok'])
const props = withDefaults(defineProps<BwModalFormProps>(), {
  formData: () => ({}),
  formRules: () => [],
  col: () => ({ span: 24 }),
  wrap: () => ({
    labelCol: { span: 4 },
    wrapperCol: { span: 20 },
  }),
})

const form = ref()
const curFormData = ref<Record<string, any>>({})
const curFormProps = computed(() => {
  return {
    layout: 'horizontal',
    hideRequiredMark: props.hideRequiredMark,
  }
})

/** 表单验证 */
const onValidate = (
  name: string | number | Array<string | number>,
  status: boolean,
  errors: Array<string>
) => {
  emit('validate', name, status, errors)
}

/** 获取表单项属性 */
const getItemProps = (item: BwFormRuleModel) => {
  const labelCol = cloneDeep(props.wrap?.labelCol ?? {})
  const wrapperCol = cloneDeep(props.wrap?.wrapperCol ?? {})
  return {
    key: item.field,
    name: item.field,
    label: item.title,
    rules: item.rules,
    labelCol: Object.assign(labelCol, item.labelCol),
    wrapperCol: Object.assign(wrapperCol, item.wrapperCol),
  }
}

/** 获取表单项元素事件 */
const getComEvents = (medelField = 'value', field: string, on: any) => {
  const onEvens: any = { ...on }
  onEvens[`update:${medelField}`] = (value: any) => {
    curFormData.value[field] = value
  }
  return onEvens
}

/** 获取表单项元素属性  */
const getComProps = (
  medelField = 'value',
  field: string,
  props?: Record<string, any>
) => {
  const propData: any = {}
  propData[medelField] = unref(curFormData)[field]
  return Object.assign(propData, props ?? {})
}

defineExpose({
  /** 表单验证 */
  onValidate() {
    return new Promise<void>((resolve, reject) => {
      unref(form)
        .validate()
        .then(() => resolve())
        .catch((error) => reject(error))
    })
  },

  /** 表单提交 */
  submit(): Promise<any> {
    return new Promise<any>((resolve, reject) => {
      unref(form)
        .validate()
        .then(() => resolve(unref(curFormData)))
        .catch((error) => reject(error))
    })
  },
})

watch(
  () => props.formData,
  (newVal) => (curFormData.value = newVal ?? {}),
  { deep: true, immediate: true }
)
</script>

<style lang="less" scoped></style>
